﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Reflection;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.CompilerServices;
using System.Data;

namespace ANZ_AIM_FRAMEWORK
{
    public class MiscHelper
    {
        public const string EXECUTION_DONE = "DONE";
        public const string EXECUTION_CANCELLED = "CANCELLED";
        public const string NO_SOD_FOUND = "NO SOD FOUND";
        public const string RECORD_STATUS__ACTIVE = "A";
        public const string RECORD_STATUS__DELETED = "X";
        public const string RECORD_STATUS__NEW = "N"; // inserted to DB but the running process has not completed yet

        /// <summary>
        /// Created manually by user.
        /// </summary>
        public const string SOURCE_USER_INPUT = "U";

        /// <summary>
        /// Created automatically by system.
        /// </summary>
        public const string SOURCE_SYSTEM_CREATED = "S";

        public const int FORM_OPMODE_ADD_NEW = 11;
        public const int FORM_OPMODE_ADD_EXISTING = 12;
        public const int FORM_OPMODE_EDIT = 21;
        public const int FORM_OPMODE_DELETE = 31;
        public const int FORM_OPMODE_QUERY = 41;

        public const string DATE_FORMAT = "dd/MM/yyyy";
        public const string TIME_FORMAT = "HH:mm:ss";
        public const string DATE_TIME_FORMAT = "dd/MM/yyyy HH:mm:ss";
        public static readonly DateTime INVALID_DATE = DateTime.ParseExact("01/01/1900", "dd/MM/yyyy", null);

        public const string INTEGER_FORMAT = "##,#0";
        public const string DECIMAL_FORMAT = "##,#0.00";

        public const int TEXTBOX_MAXLENGTH_ACCOUNT = 15;
        public const int TEXTBOX_MAXLENGTH_OSDAYS = 10;
        public const int TEXTBOX_MAXLENGTH_DATE = 10;

        public static string GetClassName<T>(T item) where T : class
        {
            return typeof(T).Name;
        }

        public static string GetPropertyName<T>(T item) where T : class
        {
            return typeof(T).GetProperties()[0].Name;
        }

        public static string GetPropertyType<T>(T item) where T : class
        {
            return typeof(T).GetProperties()[0].PropertyType.ToString();
        }

        public static int GetPropertyCount<T>(T item) where T : class
        {
            return typeof(T).GetProperties().Count();
        }

        /// <summary>
        /// Perform a deep copy of the object.
        /// Binary Serialization is used to perform the copy.
        /// Source: http://stackoverflow.com/questions/78536/cloning-objects-in-c
        /// </summary>
        /// <typeparam name="T">The type of object being copied</typeparam>
        /// <param name="source">The object instance to be copied</param>
        /// <returns>The copied object</returns>
        public static T Clone<T>(T source)
        {
            try
            {
                if (!typeof(T).IsSerializable)
                {
                    throw new ArgumentException("The type must be serializable.", "source");
                }

                // Don't serialize a null object, simply return the default for that object
                if (Object.ReferenceEquals(source, null))
                {
                    return default(T);
                }

                IFormatter formatter = new BinaryFormatter();
                Stream stream = new MemoryStream();
                using (stream)
                {
                    formatter.Serialize(stream, source);
                    stream.Seek(0, SeekOrigin.Begin);
                    return (T)formatter.Deserialize(stream);
                }
            }
            catch (Exception ex) { throw; }
        }

        /// <summary>
        /// Compare each property of the old and new object of the same type.
        /// If any difference, log the change to database.
        /// Some properties are bypassed (CreatedBy, CreatedDate, LastUpdatedBy, LastUpdatedDate)
        /// </summary>
        /// <typeparam name="T">The type of old and new object</typeparam>
        /// <param name="oldObject">The old object</param>
        /// <param name="newObject">The new object</param>
        /// <param name="skipProps">The collection of properties skipped in comparison (key = property name, value = any string)</param>
        /// <returns>True, if compared properties are equal.</returns>
        /// <returns>False, if any difference in compared properties.</returns>
        public static bool Identical<T>(T oldObject, T newObject, Dictionary<string, string> skipProps = null) where T : class
        {
            try
            {
                string tmp;
                bool result = true;
                bool stop = false;
                foreach (var propOld in oldObject.GetType().GetProperties())
                {
                    if (skipProps != null && skipProps.TryGetValue(propOld.Name, out tmp))
                        continue;

                    if (propOld.Name == "CreatedBy" || propOld.Name == "CreatedDate" ||
                        propOld.Name == "LastUpdatedBy" || propOld.Name == "LastUpdatedDate")
                        continue;

                    foreach (var propNew in newObject.GetType().GetProperties())
                    {
                        if (skipProps != null && skipProps.TryGetValue(propNew.Name, out tmp))
                            continue;

                        if (propNew.Name == "CreatedBy" || propNew.Name == "CreatedDate" ||
                            propNew.Name == "LastUpdatedBy" || propNew.Name == "LastUpdatedDate")
                            continue;

                        if (propOld.Name != propNew.Name)
                            continue;
                        object oldValueObj = propOld.GetGetMethod().Invoke(oldObject, null);
                        object newValueObj = propNew.GetGetMethod().Invoke(newObject, null);
                        string oldValue = (oldValueObj != null ? oldValueObj.ToString() : "");
                        string newValue = (newValueObj != null ? newValueObj.ToString() : "");

                        // Compare numeric values if the properties are decimal (or integer)
                        // String comparison for those types may not be accurate even though numeric values are identical.
                        // For example, 12.48 will be different from 12.4800
                        decimal oldDec, newDec;
                        if (Decimal.TryParse(oldValue, out oldDec) && Decimal.TryParse(newValue, out newDec))
                        {
                            if (oldDec != newDec)
                            {
                                result = false;
                                stop = true;
                                break;
                            }
                        }
                        else
                            if (!oldValue.Equals(newValue))
                            {
                                result = false;
                                stop = true;
                                break;
                            }
                    }
                    if (stop)
                        break;
                }
                return result;
            }
            catch (Exception ex) { throw; }
        }

        public static DateTime? StringToDate(string str)
        {
            DateTime dt;
            if (DateTime.TryParseExact(str, DATE_FORMAT, null, DateTimeStyles.None, out dt))
                return dt;
            else
                return null; // INVALID_DATE;
            /*try
            {
                DateTime dt = DateTime.ParseExact(str, "dd/MM/yyyy", null);
                return dt;
            }
            catch (FormatException e)
            {
                return INVALID_DATE;
            }*/
        }

        public static DateTime? StringToDate(string str,string[] DateFormat)
        {
            DateTime dt;
            if (DateTime.TryParseExact(str, DateFormat, null, DateTimeStyles.None, out dt))
                return dt;
            else
                return null; // INVALID_DATE;
            /*try
            {
                DateTime dt = DateTime.ParseExact(str, "dd/MM/yyyy", null);
                return dt;
            }
            catch (FormatException e)
            {
                return INVALID_DATE;
            }*/
        }

        public static string DateToString(DateTime date)
        {
            if (date == null)
                return "";
            else
                return date.ToString(DATE_FORMAT);
        }

        public static string DateToString(DateTime? date)
        {
            if (date == null)
                return "";
            else
                return ((DateTime)date).ToString(DATE_FORMAT);
        }

        public static string DateToStringWordsVN(DateTime? date)
        {
            if (date == null)
                return "";
            else
            {
                DateTime dt = (DateTime)date;
                string dd = dt.ToString("dd");
                string mm = dt.ToString("MM");
                string year = dt.ToString("yyyy");
                string dateWords = "ngày " + dd + " tháng " + mm + " năm " + year;
                return dateWords;
            }
        }

        public static string DateTimeToString(DateTime? date)
        {
            if (date == null)
                return "";
            else
                return ((DateTime)date).ToString(DATE_TIME_FORMAT);
        }

        public static string TimeToString(DateTime? date)
        {
            if (date == null)
                return "";
            else
                return ((DateTime)date).ToString(TIME_FORMAT);
        }

        public static string DecimalToString(decimal? dec, string format = null)
        {
            if (dec == null)
                return null;
            else
                if (format == null)
                    return ((decimal)dec).ToString(MiscHelper.DECIMAL_FORMAT);
                else
                    return ((decimal)dec).ToString(format);
        }

        public static string IntegerToString(long? num, string format = null)
        {
            if (num == null)
                return null;
            else
                if (format == null)
                    return ((long)num).ToString(MiscHelper.INTEGER_FORMAT);
                else
                    return ((long)num).ToString(format);
        }

        public static bool IsMobilePhoneNumber(string phoneNumber)
        {
            if (phoneNumber == null || phoneNumber == "")
                return false;

            bool result = false;
            string phone = phoneNumber.Trim();
            long tmp;
            if (!Int64.TryParse(phone, out tmp))
                result = false;
            else if (phone.Length == 10 && phone.Substring(0, 2) == "09")
                result = true;
            else if (phone.Length == 11 && phone.Substring(0, 3) == "012")
                result = true;
            return result;
        }

        public static string RemoveCRLF(string input)
        {
            return input.TrimEnd('\r', '\n');
        }

        /// <summary>
        /// Check if value is null or zero for long value.
        /// </summary>
        /// <returns></returns>
        public static bool IsNullOrDefault<T>(T value)
        {
            return object.Equals(value, default(T));
        }

        /// <summary>
        /// Remove milisec in DateTime.
        /// </summary>
        /// <param name="dateTime">DateTime: dd/mm/yyyy hh:mm:ss ms ms</param>
        /// <returns>DateTime: dd/mm/yyyy hh:mm:ss</returns>
        public static DateTime? DateTimeToShort(DateTime? dateTime)
        {
            if (!dateTime.HasValue)
            {
                return dateTime;
            }

            return new DateTime(dateTime.Value.Year, dateTime.Value.Month, dateTime.Value.Day,
                                dateTime.Value.Hour, dateTime.Value.Minute, dateTime.Value.Second);
        }

        /// <summary>
        /// Capitalize the first letter of a string
        /// Source: http://www.dotnetperls.com/uppercase-first-letter
        /// </summary>
        /// <param name="s">Input string</param>
        /// <returns>Output string with first letter capitalized</returns>
        public static string ToUpperFirstLetter(string s)
        {
            try
            {
                if (string.IsNullOrEmpty(s))
                {
                    return string.Empty;
                }
                char[] a = s.ToCharArray();
                a[0] = char.ToUpper(a[0]);
                return new string(a);
            }
            catch (Exception ex) { throw; }
        }

        /// <summary>
        /// Get Random string.
        /// </summary>
        /// <param name="minLength">minimum length result (default: 6)</param>
        /// <param name="maxLength">maximum length result (default: 10)</param>
        /// <returns>random string.</returns>
        public static string GetRandomString(int minLength = 8, int maxLength = 12)
        {
            string alphabetUpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            string alphabetLowerCase = "abcdefghijklmnopqrstuvwxyz";
            string numberic = "01234567890123456789";
            string symbol = "!@#$%^&*()_-+=[{]};:<>|./?";
            string pattern = alphabetUpperCase + alphabetLowerCase + numberic + symbol;

            bool hasUpperCase = false;
            bool hasLowerCase = false;
            bool hasNumberic = false;
            bool hasSymbol = false;
            bool result = false;

            Random randomizer = new Random();
            StringBuilder stringBuilder = new StringBuilder();
            char randomChar = new char();

            do
            {
                for (int i = 0; i < minLength; i++)
                {
                    randomChar = pattern[randomizer.Next(pattern.Length)];
                    if (alphabetUpperCase.Contains(randomChar))
                    {
                        hasUpperCase = true;
                    }
                    else if (alphabetLowerCase.Contains(randomChar))
                    {
                        hasLowerCase = true;
                    }
                    else if (numberic.Contains(randomChar))
                    {
                        hasNumberic = true;
                    }
                    else if (symbol.Contains(randomChar))
                    {
                        hasSymbol = true;
                    }
                    stringBuilder.Append(randomChar);
                }
                result = hasUpperCase && hasLowerCase && hasNumberic && hasSymbol;
                if (!result)
                {
                    while (!hasUpperCase || !hasLowerCase || !hasNumberic || !hasSymbol || stringBuilder.ToString().Length < maxLength)
                    {
                        randomChar = pattern[randomizer.Next(pattern.Length)];
                        if (alphabetUpperCase.Contains(randomChar))
                        {
                            hasUpperCase = true;
                        }
                        else if (alphabetLowerCase.Contains(randomChar))
                        {
                            hasLowerCase = true;
                        }
                        else if (numberic.Contains(randomChar))
                        {
                            hasNumberic = true;
                        }
                        else if (symbol.Contains(randomChar))
                        {
                            hasSymbol = true;
                        }
                        stringBuilder.Append(randomChar);
                    }
                }
                result = hasUpperCase && hasLowerCase && hasNumberic && hasSymbol;
            } while (!result);

            return stringBuilder.ToString();
        }

        public static DataTable ChangeColumnNameByMap(DataTable tableToChangeColumnName, Dictionary<string, string> ColumnMap)
        {
            try
            {
                foreach (var item in ColumnMap)
                {
                    if (tableToChangeColumnName.Columns[item.Key] != null)
                    {
                        if (tableToChangeColumnName.Columns[item.Value] == null)
                        {
                            tableToChangeColumnName.Columns[item.Key].ColumnName = item.Value;
                        }
                    }
                }

                return tableToChangeColumnName;
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}
